//
//  XSSocketManager.m
//  XSSocketDemo
//
//  Created by xingshuai on 2021/8/12.
//

#import "XSSocketManager.h"

// 接受SRWebSocketDelegate
@interface XSSocketManager() <SRWebSocketDelegate>
// SRWebSocket
@property (nonatomic,strong)SRWebSocket *socket;
// 发送ping的计时器
@property(nonatomic,strong)NSTimer *pingTimer;
// 重新连接的计时器
@property(nonatomic,strong)NSTimer *reconnetTimer;

@end

static const NSTimeInterval WebSocketHeartBeatTimeInterval = 10.0;

@implementation XSSocketManager

// 单例方法
static XSSocketManager * instance = nil;

+ (instancetype)sharedInstance {
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        instance = [[super allocWithZone:NULL] init];
    }) ;
    return instance ;
}

+ (id)allocWithZone:(struct _NSZone *)zone {
    return [XSSocketManager sharedInstance];
}
- (id)copyWithZone:(struct _NSZone *)zone {
    return [XSSocketManager sharedInstance];
}

#pragma mark SRWebSocket  Open&Close&Send
// 连接webSocket
- (void)connect {
    self.socket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.wsURLString]]];
    self.socket.delegate = self;
    [self.socket open];
    NSLog(@"[方法:%s-行数:%d]WebSocket_Host_URL:%@",__FUNCTION__,__LINE__,self.wsURLString);
}

// 发送消息的方法
- (BOOL)sendString:(NSString *)string error:(NSError **)error {
    // webSocket没有打开的状态下
    if (self.socket.readyState != SR_OPEN) {
        if (self.delegate&&[self.delegate respondsToSelector:@selector(webSocketDidClose:)]) {
            [self.delegate webSocketDidClose:self.socket];
        }
        NSLog(@"发送json时webSocket没有打开!");
        return NO;
    }
    if ([self trimEmpty:string]) {
        NSLog(@"[方法:%s-行数:%d]发送json数据为空!",__FUNCTION__,__LINE__);
        return NO;
    }
    NSLog(@"\n[方法:%s-行数:%d]\n发送消息:\n%@\n",__FUNCTION__,__LINE__,string);
    [self.socket send:string];
    return YES;
}

#pragma mark - SRWebSocketDelegate
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
    NSString *aMessage = (NSString*)message?:@"";
    if (![self trimEmpty:aMessage]) {
        NSDictionary *dic = @{@"message":aMessage};
        NSLog(@"webSocket根源收到的消息:%@",dic);
    } else {
        NSLog(@"[方法:%s-行数:%d] message is null !",__FUNCTION__,__LINE__);
    }
    // 收到消息代理回调
    if (self.delegate&&[self.delegate respondsToSelector:@selector(webSocket:didReceiveMessage:)]) {
        [self.delegate webSocket:webSocket didReceiveMessage:message];
    }
}

// webSocket已经打开回调
- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
    NSLog(@"[方法:%s-行数:%d]\nwebSocketDidOpen!\n",__FUNCTION__,__LINE__);
    // 打开socket需要发送一条数据
    // 传给服务器端socket的参数拼接
    NSString *strJson = [self currentDataJsonString:@"12"];
    NSError *error;
    [self sendString:strJson error:&error];
    
    if (self.delegate&&[self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) {
        [self.delegate webSocketDidOpen:webSocket];
    }
    
    __weak typeof(self) weakSelf = self;
    // webSockeet连接每10秒发送一个Ping指令
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [weakSelf startPing];
    });
    
}

// 收到连接错误的回调
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
    NSLog(@"[方法:%s-行数:%d] [webSocket connect fail error resson:]%@\n[closed createTime]%@[closed host]%@\n",__FUNCTION__, __LINE__,error.description,[NSDate date],webSocket.url);
    if (self.delegate&&[self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) {
        [self.delegate webSocket:webSocket didFailWithError:error];
    }
}

// webSocket断开连接代理回调
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(nullable NSString *)reason wasClean:(BOOL)wasClean {
    NSLog(@"[方法:%s-行数:%d][webSocketClosed with reason:]%@\n[closed createTime:]%@\n[closed host:]%@\n" ,__FUNCTION__, __LINE__,reason,[NSDate date],webSocket.url);
    if (self.delegate&&[self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
        [self.delegate webSocket:webSocket didCloseWithCode:code reason:reason wasClean:wasClean];
    }
}

// 服务器心跳回调
- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(nullable NSData *)pongData {
    if (self.delegate&&[self.delegate respondsToSelector:@selector(webSocket:didReceivePong:)]) {
        [self.delegate webSocket:webSocket didReceivePong:pongData];
    }
}

#pragma -mark Heartbeat
// 启动socket心跳定时器
-(void)startPing {
    if (_pingTimer) {
        [_pingTimer invalidate];
        _pingTimer = nil;
    }
    
    if (_reconnetTimer) {
        [_reconnetTimer invalidate];
        _reconnetTimer = nil;
    }
    _pingTimer = [NSTimer scheduledTimerWithTimeInterval:WebSocketHeartBeatTimeInterval target:self selector:@selector(sendPing:) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:_pingTimer forMode:NSRunLoopCommonModes];
}

// 发送一个心跳消息ping，参数可以为nil（用来确定客户端未丢失。）
-(void)sendPing:(id)sender {
    // 传给服务器端socket的参数拼接
    NSString *strJson = [self currentDataJsonString:@"0"];
    NSLog(@"客户端socket还在");
    if (self.socket.readyState == SR_OPEN) {
        NSError *error;
        //[self.socket sendPing:nil];
        [self sendString:strJson error:&error];
        if (error) {
            NSLog(@"%s:%d %@", __FUNCTION__, __LINE__,error);
        }
    } else {
        [_pingTimer invalidate];
        _pingTimer = nil;
        [self reconnect];
    }
}

// 停止心跳定时器
- (void)destoryHeartBeat {
    if (_pingTimer) {
        [_pingTimer invalidate];
        _pingTimer = nil;
    }
}

#pragma -mark Reconnect
-(void)reconnect {
    // 连接
    NSLog(@"[%s:%d]reconnecting! ",__FUNCTION__,__LINE__);
    //[self closeWebSocket];
    [self connect];
    if (_reconnetTimer) {
        [_reconnetTimer invalidate];
        _reconnetTimer = nil;
    }
    _reconnetTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(startReconnect) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:_reconnetTimer forMode:NSRunLoopCommonModes];
}

-(void)startReconnect {
    self.socket = nil;
    [self connect];
    NSLog(@"%s:%d socket reconnecting!", __FUNCTION__, __LINE__);
}

// 关闭socket/停止定时器
-(void)closeWebSocket {
    if (self.socket) {
        if (_reconnetTimer) {
            [_reconnetTimer invalidate];
            _reconnetTimer = nil;
        }
                
        if (self.socket.readyState < 2) {
            [self.socket close];
            self.socket = nil;
            [self destoryHeartBeat];
        }
    }
}

- (BOOL)trimEmpty:(NSString *)str {
    if (!str) {
        return YES;
    }
    if ([str isKindOfClass:[NSNull class]]) {
        return YES;
    }
    NSString *trimStr = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    return (trimStr == nil || trimStr.length <= 0);
}

#pragma -mark util
// 拼接需要的数据为字符串类型json （这里模拟传一条json类型的字符串）
-(NSString *)currentDataJsonString:(NSString *)activityType {
    NSMutableDictionary *sendDic = [[NSMutableDictionary alloc] init];
    [sendDic setValue:@"" forKey:@"groupId"];
    [sendDic setValue:@"" forKey:@"userId"];
    [sendDic setValue:activityType forKey:@"activityType"];
    [sendDic setValue:@"" forKey:@"limit"];
    [sendDic setValue:self.liveId forKey:@"liveId"];
    NSString *strJson = [sendDic yy_modelToJSONString];
    return strJson;
}

@end
